package com.mall.filter;

import com.mall.entity.LoginUser;
import com.mall.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    private final static int RENEWAL_TIME = 600;     // 续签时间阈值，单位是秒
    @Autowired
    RedisTemplate redisTemplate;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//        //获取头信息，检查是否有令牌
//        String token = request.getHeader("token");需要和前端配合，有条件再实现从消息头获取token
        // 从Cookie中获取sessionId(暂时使用如下代码)

        Cookie[] cookies = request.getCookies();
        Claims claims;
        String token = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("sessionId")) {
                    token = cookie.getValue();
                }
            }
        }
        if (StringUtils.isEmpty(token)){
            //令牌为空将不再security上下文中存放对象，再后面的过滤器会抛出异常
            filterChain.doFilter(request,response);
            return;
        }
        String userId;
        try {
            claims = JwtUtil.parseJWT(token);
            userId = claims.getSubject();//解析出用户信息
        } catch (Exception e) {
            e.printStackTrace();
            throw new RemoteException("token非法！");
        }
        //再缓存中获取用户信息
        LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get("token:"+userId);
        if (loginUser == null) {
            // Redis中未找到用户信息，说明该用户已经强制下线，需要从Spring Security上下文中删除用户信息
            SecurityContextHolder.clearContext();
            throw new RemoteException("用户已离线");
        }
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                //构造函数选择三个参数表示以及认证成功（两个的话第一个是用户名，第二个是密码）
                new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        //将用户信息（验证通过）存放到上下文中
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        // 检查令牌的有效时间，如果过期时间小于续签时间阈值，则自动续签令牌
        Date expiration = claims.getExpiration();
        long remain = expiration.getTime() - System.currentTimeMillis();
        if (remain < RENEWAL_TIME&&remain>0) {
            // 生成新的令牌
            String newToken = JwtUtil.createJWT(userId);
            // 更新Redis中的用户信息和令牌信息
            redisTemplate.opsForValue().set("token:"+userId,loginUser,60, TimeUnit.MINUTES);
            Cookie cookie = new Cookie("sessionId", newToken); //
            cookie.setMaxAge(180); // 设置 Cookie 的过期时间，单位为秒。此处设置30秒;
            cookie.setPath("/");
            response.addCookie(cookie);  // 写入新的令牌到Cookie中
        }
        filterChain.doFilter(request,response);
    }
}
